/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2018年8月16日
 * V4.0
 */
package com.jphenix.one.tools;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.imageio.ImageIO;

import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.api.IRequest;
import com.jphenix.standard.servlet.api.IResponse;

/**
 * 图片验证码动作
 * 
 * 调用方法(image.jsp)：
 * 
 *  //生成随机图片验证码，并将图片中的验证码值保存到会话中（主键为：randimage)
 *  DynamicImage.show(request,response);
 * 
 * 页面显示方法：
 * <img src="/image.jsp" />
 * 
 * 验证方法：
 * 
 *   String.valueOf(request.getParameter("img_code")).equals(request.getSession().getAttribute("randimage"))
 * 
 * 
 * 2022-09-04 隔离了ServletApi，兼容新老Tomcat
 * 
 * 
 * @author MBG
 * 2018年8月16日
 */
@ClassInfo({"2022-09-04 21:58","图片验证码动作"})
public class DynamicImage {


	//随机处理类
    protected Random random = new Random();
	
    protected int molestLineCount          = 15;          //干扰线数量
    protected int defaultStringLength      = 4;           //默认随机字符数量
    protected int defaultImageWidth        = 60;          //默认图片宽
    protected int defaultImageHeight       = 20;          //默认图片高
    protected String defaultSessionKey     = "randimage"; //默认随机字符串放入会话中的主键
    protected String randStringSessioonKey = null;        //随机号的会话主键
    protected int defaultFrontColor        = 200;         //默认前景色
    protected int defaultBackColor         = 250;         //默认背景色
    protected int imageWidth               = 0;           //图片宽
    protected int imageHeight              = 0;           //图片高
    protected int frontColor               = 0;           //图片前景色
    protected int backColor                = 0;           //图片背景色
    protected int randStringLength         = 0;           //随机字符串长度
    
	/**
	 * 构造函数
	 * @author MBG
	 */
	public DynamicImage() {
		super();
	}
	
	/**
	 * 显示随机验证码（默认保存到会话中的验证码值主键为：randimage）
	 * @param req        页面请求
	 * @param resp       页面输出
	 * @throws Exception 异常
	 * 2018年8月16日
	 * @author MBG
	 */
	public static void show(IRequest req,IResponse resp) throws Exception {
		show(req,resp,null);
	}
	
	/**
	 * 显示随机验证码
	 * @param req        页面请求
	 * @param resp       页面输出
	 * @param sessionKey 指定保存到会话中的图片验证码值主键
	 * @throws Exception 异常
	 * 2018年8月16日
	 * @author MBG
	 */
	public static void show(IRequest req,IResponse resp,String sessionKey) throws Exception {
		//构建绘图类实例
		DynamicImage di = new DynamicImage();
		if(sessionKey!=null && sessionKey.length()>0) {
			di.randStringSessioonKey = sessionKey;
		}
		di.createRandImageAction(req,resp);
	}
	

	/**
	 * 建立随机图片,并将随机号放入session中(主键为defaultSessionKey)
	 * @author 刘虻
	 * 2007-10-12下午12:04:42
	 * @param req 页面请求
	 * @param resp 页面反馈
	 * @throws Exception 执行发生异常
	 */
	public String createRandImageAction(IRequest req,IResponse resp) throws Exception {
		//执行生成图片
		return createRandImageAction(
			 	 req
				,resp
				,getRandStringLength()
				,getImageWidth()
				,getImageHeight());
	}
	
	/**
	 * 建立随机图片,并将随机号放入session中(主键为defaultSessionKey)
	 * @author 刘虻
	 * 2007-10-12下午12:04:42
	 * @param req         页面请求
	 * @param resp        页面反馈
	 * @param strLength   随机字符串长度
	 * @param imageWidth  图片宽
	 * @param imageHeight 图片高
	 * @throws Exception  执行发生异常
	 */
	public String createRandImageAction(
			 IRequest  req
			,IResponse resp
			,int       strLength
			,int       imageWidth
			,int       imageHeight) throws Exception {
		//获取随机字符串
		String randStr = getRandString(strLength);
		//获取随机图片
		BufferedImage image = getRandImage(randStr,imageWidth,imageHeight);
		//放入会话
		req.iGetSession().setAttribute(getRandStringSessioonKey(), randStr);
		//设置允许输出图片
		System.setProperty("java.awt.headless","true");
		resp.flushBuffer();
		resp.setContentType("image/jpeg");
		resp.setHeader("Cache-Control", "no-cache");
		resp.setHeader("Pragma", "no-cache");
		resp.setDateHeader("Expires", 0); 
		
		//输出图象到页面
		ImageIO.write(image,"",resp.iGetOutputStream());
		
		return randStr;
	}
	
	/**
	 * 获取随机图片
	 * @author 刘虻
	 * 2007-10-12下午12:08:41
	 * @param randStr 图片中的随机字符串
	 * @param width 图片宽
	 * @param height 图片高
	 * @return 随机图片
	 */
	public BufferedImage getRandImage(String randStr,int width,int height) {
		
		//构造返回值
		BufferedImage image = 
			new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

		//获取图形上下文
		Graphics2D graphics = (Graphics2D)image.getGraphics();

		//设定背景色
		graphics.setColor(getRandColor(getFrontColor(),getBackColor()));
		graphics.fillRect(0, 0, width, height);
		
		//设定字体
		graphics.setFont(new Font("Georgia, Times New Roman, Times, serif",Font.PLAIN,18));
		
		//随机产生干扰线，使图象中的认证码不易被其它程序探测到
		graphics.setStroke(new BasicStroke(2.0f)); //设置线条粗细
		for (int i=0;i<molestLineCount;i++){
			//干扰线坐标
			int x = random.nextInt(width);
			int y = random.nextInt(height);
			int xl = random.nextInt(12);
			int yl = random.nextInt(12);
			graphics.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
			graphics.drawLine(x,y,x+xl,y+yl);
		}
		
		if (randStr!=null) {
			int size = randStr.length();
			
			for (int i=0;i<size;i++) {
				//获取一个字符
				String str = randStr.substring(i,i+1);
			    // 将认证码显示到图象中
				//调用函数出来的颜色相同，可能是因为种子太接近，所以只能直接生成
				graphics
					.setColor(
							new Color(
									20+random.nextInt(110)
									,20+random.nextInt(110)
									,20+random.nextInt(110)));
				graphics.drawString(str,13*i+6,16);
			}
		}
		
		//图象生效
		graphics.dispose();
		
		return image;
	}
	
	/**
	 * 获取随机字符串
	 * @author 刘虻
	 * 2007-10-12上午11:31:00
	 * @param length 字符串长度
	 * @return 随机字符串
	 */
	public String getRandString(int length) {
		//构造返回值
		StringBuffer reSbf = new StringBuffer();
		for (int i=0;i<length;i++){
			reSbf.append(random.nextInt(10));
		}
		return reSbf.toString();
	}
	
	/**
	 * 设置图片高
	 * @author 刘虻
	 * 2007-10-12下午12:34:19
	 * @param imageHeight 图片高
	 */
	public void setImageHeight(int imageHeight) {
		this.imageHeight = imageHeight;
	}
	
	/**
	 * 获取图片高
	 * @author 刘虻
	 * 2007-10-12下午12:34:32
	 * @return 图片高
	 */
	public int getImageHeight() {
		if (imageHeight<1) {
			imageHeight = defaultImageHeight;
		}
		return imageHeight;
	}
	
	/**
	 * 获取图片宽
	 * @author 刘虻
	 * 2007-10-12下午12:34:52
	 * @return 图片宽
	 */
	public int getImageWidth() {
		if (imageWidth<1) {
			imageWidth = defaultImageWidth;
		}
		return imageWidth;
	}
	
	/**
	 * 设置图片宽
	 * @author 刘虻
	 * 2007-10-12下午12:34:39
	 * @param imageWidth 图片宽
	 */
	public void setImageWidth(int imageWidth) {
		this.imageWidth = imageWidth;
	}
	
	/**
	 * 获取随机字符串长度
	 * @author 刘虻
	 * 2007-10-12下午12:33:20
	 * @return 随机字符串长度
	 */
	public int getRandStringLength() {
		if (randStringLength<1) {
			randStringLength = defaultStringLength;
		}
		return randStringLength;
	}
	
	/**
	 * 设置随机字符串长度
	 * @author 刘虻
	 * 2007-10-12下午12:33:30
	 * @param randStringLength 随机字符串长度
	 */
	public void setRandStringLength(int randStringLength) {
		this.randStringLength = randStringLength;
	}
	
	/**
	 * 构造随机颜色
	 * @author 刘虻
	 * 2007-10-12上午11:24:04
	 * @param fColor 前景颜色
	 * @param bColor 背景颜色
	 * @return 随机颜色
	 */
	protected Color getRandColor(int fColor,int bColor){
		if(fColor>255) {
        	fColor=255;
        }
        if(bColor>255) {
        	bColor=255;
        }
        int r=fColor+random.nextInt(bColor-fColor); //红
        int g=fColor+random.nextInt(bColor-fColor); //绿
        int b=fColor+random.nextInt(bColor-fColor); //蓝
        return new Color(r,g,b);
	}
	
	/**
	 * 获取前景色
	 * @author 刘虻
	 * 2007-10-12下午12:33:57
	 * @return 前景色
	 */
	public int getFrontColor() {
		if (frontColor<1) {
			frontColor = defaultFrontColor;
		}
		return frontColor;
	}
	
	/**
	 * 设置前景色
	 * @author 刘虻
	 * 2007-10-12下午12:34:14
	 * @param frontColor 前景色
	 */
	public void setFrontColor(int frontColor) {
		this.frontColor = frontColor;
	}
	
	/**
	 * 获取背景色
	 * @author 刘虻
	 * 2007-10-12下午12:33:38
	 * @return 背景色
	 */
	public int getBackColor() {
		if (backColor<1) {
			backColor = defaultBackColor;
		}
		return backColor;
	}
	
	/**
	 * 设置背景色
	 * @author 刘虻
	 * 2007-10-12下午12:33:49
	 * @param backColor 背景色
	 */
	public void setBackColor(int backColor) {
		this.backColor = backColor;
	}
	
	/**
	 * 设置随机号的会话主键
	 * @author 刘虻
	 * 2007-10-12下午12:19:18
	 * @param randStringSessioonKey 随机号的会话主键
	 */
	public void setRandStringSessioonKey(String randStringSessioonKey) {
		this.randStringSessioonKey = randStringSessioonKey;
	}
	
	/**
	 * 获取随机号的会话主键
	 * @author 刘虻
	 * 2007-10-12下午12:19:24
	 * @return 随机号的会话主键
	 */
	public String getRandStringSessioonKey() {
		if (randStringSessioonKey==null) {
			randStringSessioonKey = defaultSessionKey;
		}
		return randStringSessioonKey;
	}
}
